/**
 * Defines a generic item
 * 
 * @author remo.lemma@lu.unisi.ch and patrick.zulian@lu.unisi.ch
 */

package ch.unisi.inf.projects.java.uom.elements;

// Libraries
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.List;
import java.util.ArrayList;

import ch.unisi.inf.projects.java.uom.Constants;
import ch.unisi.inf.projects.java.uom.Database;
import ch.unisi.inf.projects.java.uom.Pair;
import ch.unisi.inf.projects.java.uom.exceptions.UOMException;
import ch.unisi.inf.projects.java.uom.exceptions.UOMExceptions;

public class Item extends Element{
	
	/** Item's ItemClass */
	private ItemClass itemClass;
		
	/**
	 * Constructor for an item, that should used directly only to load an item
	 * @param name the name of the object (ItemClass_ID)
	 * @param itemClass the class of the item
	 * @param values the map of field to value
	 * @param modified a boolean that represents the state of the Item
	 */
	public Item (final String name, final ItemClass itemClass, final Map<String,String> values, final boolean modified)
	{
		super(name, values, modified);
		this.itemClass = itemClass;
	}
	
	/**
	 * Constructor for a new empty item
	 * @param newId the id to be taken (not DB id, but new-Item id)
	 * @param itemClass the itemClass of the new item
	 */
	public Item (final int newId, final ItemClass itemClass)
	{
		this(itemClass.getName().toUpperCase() + Constants.ITEM_PARSECHAR.value() + Constants.ITEM_NEW.value() + Constants.ITEM_PARSECHAR.value() + newId, itemClass, new HashMap<String,String>(), true);
		this.initializeNewItem();

	}
	
	/**
	 * It initializes a new item
	 */
	private void initializeNewItem()
	{
		final Set<String> fields = itemClass.getFields().keySet();
		for(String field : fields)
			addField(field, "");
	}
	/**
	 * Returns the ItemClass of the item
	 * @return the category
	 */
	public ItemClass getItemClass()
	{
		return this.itemClass;
	}
	
	/**
	 * Checks if two strings are of the same UOM-formatted type
	 * @param s1 the first string
	 * @param s2 the second string
	 * @return true if they are the of the same type, false otherwise
	 */
	public boolean sameType (String s1, String s2)
	{
		final String c = Constants.ITEM_PARSECHAR.value();
		final String[] splits1 = s1.split(c);
		final String[] splits2 = s2.split(c);
		
		if (splits1.length == 0 || splits2.length == 0)
			return false;
		return (splits1[0].equals(splits2[0])); 
	}
	
	/**
	 * gets the elements of the same list
	 * @param listName the name of the list
	 * @return an hashMap<String,String> With the elements of the same list
	 */
	public HashMap<String,String> getListElements(final String listName)
	{
		HashMap<String,String> listElements = new HashMap<String,String>();
		
		for(Map.Entry<String,String> e : getFields().entrySet())
		{	
			if(e.getKey().equals(listName))
				listElements.put(e.getKey(),e.getValue());
		}
		return listElements;
	}
	
	/**
	 * Modifies the value of a field
	 * @param field the field name
	 * @param newValue the new value of the field
	 */
	public void setField (final String field, final String newValue)
	{
		if (field == null || newValue == null || field.equals(Constants.ITEM_PRIMARY_KEY.value()) || !getFields().keySet().contains(field.toUpperCase()) || newValue.equals(getFields().get(field.toUpperCase())))
			return;
		getFields().put(field.toUpperCase(), newValue);
		getUpdates().add(new Pair<String,String>(field.toUpperCase(), newValue));
		setModified(true);
	}
	
	/**
	 * Removes a field (setting the value to null)
	 * @param field the name of the field
	 */
	public void removeField (final String field)
	{
		this.setField(field, null);
	}
	
	/**
	 * Gets a out the value of the field
	 * @param field the name of the field
	 */
	public String getField (final String field)
	{
		return getFields().get(field.toUpperCase());
	}
	
	/**
	 * Saves an Item in the database (add or update)
	 * @throws Exception handles the needed exceptions
	 */
	public void save() throws Exception
	{
		Database.instance().itemSave(this);
	}
	
	/**
	 * Deletes an Item from the database
	 * @throws Exception handles the needed exceptions
	 */
	public void delete() throws Exception
	{
		Database.instance().itemDelete(this);
	}
	
	/**
	 * Checks if the Item has been just created
	 * @return true if the Item has been just created, false otherwise 
	 * @throws UOMException
	 */
	public boolean isNew() throws UOMException
	{
		String[] splits = getName().split(Constants.ITEM_PARSECHAR.value());
		if (splits.length < 2)
			throw new UOMException(UOMExceptions.MALFORMED_DATA.code(), "Malformed data detected");
		return splits[1].equals(Constants.ITEM_NEW.value());
	}
	
	/**
	 * Sets a new id for the Item (should not be called unless the item is new)
	 * @param id the new id
	 */
	public void setId (final String id)
	{
		getFields().put(Constants.ITEM_PRIMARY_KEY.value(), id);
		this.setName(itemClass.getName() + Constants.ITEM_PARSECHAR.value() + id);
	}
	
	/**
	 * Gets the id of the Item
	 * @return the id
	 */
	public String getId ()
	{
		return this.getField(Constants.ITEM_PRIMARY_KEY.value());
	}
	
	/**
	 * Returns the preview for this Item
	 * @return the preview (Multimedia object) for this item or null
	 * @throws Exception handles the needed exceptions
	 */
	public Multimedia getPreview() throws Exception
	{
		final String preview = this.getField("MULTIMEDIA_PREVIEW");
		if (preview == null || preview.equals(""))
			return null;
		return ItemManager.instance().multimediaFromId(preview);
	}
	
	/**
	 * Returns a list of Photos associated with this Item
	 * @return the list of Photos
	 */
	public List<Photo> getPhotos()
	{
		final List<Photo> photos = new ArrayList<Photo>();
		final String[] ids = this.getField("LISTMULTIMEDIA_MULTIMEDIA").split(Constants.ITEM_PARSECHAR.value());
		for (final String id : ids)
		{
			try {
				final Photo m = (Photo) ItemManager.instance().multimediaFromId(id);
				if (m != null) {
					photos.add(m);
				}
			}
			catch (final ClassCastException cce)
			{
				continue;
			}
			catch (final Exception e)
			{
				// TODO
			}
		}
		return photos;
	}
}
